package cn.com.chengq.handler;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;

import java.util.Arrays;
import java.util.Date;
import java.util.List;


/**
 * 切面类的引用注解 @Aspect
 *
 * 定义切面功能, 即动态代理的功能。
 */
@Aspect
public class MyAspect {

    /**
     * 前置通知方法的定义
     * 1. 方法是public
     * 2. 方法是void
     * 3. 方法的名称可以自定义
     * 4. 方法可以有参数，如果有是JoinPoint, 也可以没有
     * 5. 如果有JoinPoint类型的参数，必须是方法的第一个参数，作业是获取方法执行时的信息
     */
//    @Before("execution(public void cn.com.chengq.service.impl.SomeServiceImpl.doSome(String, Integer))")
//    public void myBefore() {
//        System.out.println("前置通知：" + new Date());
//    }

//    @Before("execution(void cn.com.chengq.service.impl.SomeServiceImpl.doSome(String, Integer))")
//    public void myBefore() {
//        System.out.println("前置通知：" + new Date());
//    }

    @Before(value="execution(void *..SomeService.do*(..)) && args(name, age)", argNames = "name,age")
    public void myBefore1(String name, Integer age) {
        // System.out.println("前置通知1： 获取方法的签名: " + joinPoint.getSignature());
        System.out.println("前置通知1： 方法的参数 " + "name=" + name + ", age=" + age);
        System.out.println("前置通知1：" + new Date());
    }

    @Before(value="execution(void *..SomeService.do*(..)) && args(name, age)", argNames = "joinPoint,name,age")
    public void myBefore3(JoinPoint joinPoint, String name, Integer age) {
        System.out.println("前置通知1： 获取方法的签名: " + joinPoint.getSignature());
        System.out.println("前置通知1： 方法的参数 " + "name=" + name + ", age=" + age);
        System.out.println("前置通知1：" + new Date());
    }

//    @Before("execution(void *..SomeServiceImpl.do*(..))")
//    public void myBefore2() {
//        System.out.println("前置通知2：" + new Date());
//    }

    /**
     * 后置通知 @AfterReturning
     * 1. 方法是public
     * 2. 方法是void
     * 3. 方法的名称可以自定义
     * 4. 方法可以有参数，推荐使用Object类型
     *
     * @param res 表示目标方法的返回值
     */
    @AfterReturning(value = "execution(* *..SomeService.doOther(..)) && args(name, age)", returning = "res", argNames = "res,name,age")
    public void myAfterReturning(Object res, String name, Integer age) {
        System.out.println("后置返回通知1: " + res);
        ((List)res).add("NG");
        System.out.println("后置返回通知1: 方法的参数 " + "name=" + name + ", age=" + age);
    }

    @AfterReturning(value = "execution(* *..SomeService.doInteger(..)) && args(name, age)", returning = "res", argNames = "jp,res,name,age")
    public void myAfterReturning2(JoinPoint jp, Object res, String name, Integer age) {
        System.out.println("后置返回通知1: " + res);
        // ((Integer) res)
        System.out.println("后置返回通知1: 方法的参数 " + "name=" + name + ", age=" + age);
    }

//    @Around(value="execution(* *..SomeService.doFirst(..)) && args(name,age)", argNames = "pjp,name,age")
//    public Object myAround(ProceedingJoinPoint pjp, String name, Integer age) throws Throwable {
//        System.out.println("函数执行时的参数: " + "name=" + name + ", age=" + age);
//        System.out.println("环绕通知前1：");
//        Object ret = pjp.proceed();
//        System.out.println("环绕通知后1：");
//        return "切面方法的返回";
//    }

    /**
     * 用于声明切点表达式，已到达重复利用和简化声明
     * @param name
     * @param age
     */
    @Pointcut(value="execution(* *..SomeService.do*(..)) && args(name,age)", argNames = "name,age")
    private void myPointcut(String name, Integer age){}

    @Around(value="myPointcut(name,age)")
    public Object myAround2(ProceedingJoinPoint pjp, String name, Integer age) throws Throwable {
        System.out.println("函数执行时的参数: " + "name=" + name + ", age=" + age);
        System.out.println("环绕通知前1：");
        Object ret = pjp.proceed();
        System.out.println("环绕通知后1：");
        return "切面方法的返回";
    }

    /**
     * 最终通知, 执行位置类似finally语句中
     */
    @After("execution(* *..SomeService.do*(..))")
    public void myAfter(JoinPoint joinPoint) {
        System.out.println("最终通知1：");
    }

    /**
     *
     * @param ex
     */
    @AfterThrowing(value = "execution(* *..SomeService.doThird(..))", throwing = "ex")
    public void myAfterThrowing(Throwable ex) {
        System.out.println("异常通知1:");
    }

    @Around("execution(Integer *..SomeService.addNumber(..))")
    public Object checkParameters(ProceedingJoinPoint pjp) throws Throwable {
        int ret = -1;
        System.out.println("环绕通知执行前");
        if (Arrays.asList(pjp.getArgs()).stream().allMatch((item) -> item != null && (Integer)item >= 0)) {
            ret = (Integer) pjp.proceed();
        }

        System.out.println("环绕通知执行前");
        return ret;
    }

}
